/*
 * @Author: huangwei
 * @Date: 2021-05-17 14:49:10
 * @LastEditors: 黄威
 * @LastEditTime: 2022-03-11 09:41:38
 * @Description: 地图操作相关
 */
import { Component, Vue, Prop } from 'vue-property-decorator'

import * as Cesium from 'Cesium'
import { Viewer, viewerPix2Coord, toCentoid, toBoundary } from '@/utils/view'

@Component
export default class ViewMixin extends Vue {
    map!: Viewer

    /**
     * 图幅缩放（用于控制和二维视图的层级同步）
     */
    @Prop({ type: Number, default: 1 })
    scale!: number

    /**
     * @public
     * 地图容器变化调用的接口
     */
    resize() {
        this.map.resize()
    }

    /**
     * @public
     * 交互是否激活（即鼠标是否可以拖动地图）
     * @param active
     */
    setInteractiveActive(active: boolean = true) {
        this.map.scene.screenSpaceCameraController.enableRotate = active
        this.map.scene.screenSpaceCameraController.enableTranslate = active
    }

    /**
     * @public
     * 获取当前缩放层级
     * @returns {Number}
     */
    getZoom(): number {
        return this.map.vc.getZoomFromHeight(
            this.map.scene.camera.positionCartographic.height
        )
    }

    /**
     * @public
     * 获取当前视图范围
     * @returns {Number[]}
     */
    getBounds(): number[] {
        const scene = this.map.scene
        const ellipsoid = scene.globe.ellipsoid
        const canvas = scene.canvas

        let car3_lt = null
        let xIndex = 0
        let yIndex = 0
        do {
            car3_lt = this.map.camera.pickEllipsoid(
                new Cesium.Cartesian2(xIndex, yIndex),
                ellipsoid
            )

            // 这里每次10像素递加，一是10像素相差不大，二是为了提高程序运行效率
            xIndex <= canvas.width && (xIndex += 10)
            yIndex <= canvas.height && (yIndex += 10)
        } while (!car3_lt)

        let car3_rb = null
        xIndex = canvas.width
        yIndex = canvas.height
        do {
            car3_rb = this.map.camera.pickEllipsoid(
                new Cesium.Cartesian2(xIndex, yIndex),
                ellipsoid
            )

            // 这里每次10像素递加，一是10像素相差不大，二是为了提高程序运行效率
            yIndex >= 10 && (yIndex -= 10)
            xIndex >= 10 && (xIndex -= 10)
        } while (!car3_rb)

        const carto_lt = ellipsoid.cartesianToCartographic(car3_lt)
        const carto_rb = ellipsoid.cartesianToCartographic(car3_rb)
        return [
            Cesium.Math.toDegrees(carto_lt.longitude),
            Cesium.Math.toDegrees(carto_rb.latitude),
            Cesium.Math.toDegrees(carto_rb.longitude),
            Cesium.Math.toDegrees(carto_lt.latitude),
        ]
    }

    /**
     * @public
     * 放大
     */
    zoomIn() {
        this.map.vc.zoomToTargetZoom(this.getZoom() + 1)
    }

    /**
     * @public
     * 缩小
     */
    zoomOut() {
        this.map.vc.zoomToTargetZoom(this.getZoom() - 1)
    }

    /**
     * @public
     * 定位到范围 [minx, miny, maxx, maxy]
     * @param {Number[]} extent 范围
     * @param {any} options 定位参数
     */
    zoom2Extent(extent: any, options?: any) {
        extent = toBoundary(extent)

        this.map.camera.flyTo({
            destination: Cesium.Rectangle.fromDegrees(
                extent[0],
                extent[1],
                extent[2],
                extent[3]
            ),
            ...options,
        })
    }

    /**
     * @public
     * 定位到中心点 [lng, lat]
     * @param {Number[]} centoid 中心点
     * @param {any} options 定位参数
     */
    zoom2Centoid(centoid: any, options?: any) {
        centoid = toCentoid(centoid)

        // 高度信息
        const height =
            options && options.zoom >= 0
                ? this.map.vc.getHeightFromZoom(options.zoom)
                : centoid[2]
                ? centoid[2]
                : this.map.camera.positionCartographic.height

        // 设置中心点（保留相机姿态）
        if (!options || !options.duration || options.duration <= 0) {
            const { heading, pitch } = this.map.camera
            this.map.camera.lookAt(
                Cesium.Cartesian3.fromDegrees(centoid[0], centoid[1], 0),
                new Cesium.HeadingPitchRange(heading, pitch, height)
            )
            this.map.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
        }
        // 这种模式设置的是相机位置（加上姿态，地图中心点可能不在想要的位置）
        else {
            this.map.camera.flyTo({
                destination: Cesium.Cartesian3.fromDegrees(
                    centoid[0],
                    centoid[1],
                    height
                ),
                ...options,
            })
        }
    }

    /**
     * @public
     * 定位到几何
     * @param {any} geom 请查看 `L.Layer` `Cesium.Entity`
     * @param {any} options 定位参数
     */
    zoom2Geometry(geom: any, options?: any) {
        let target = geom

        // 常规类型，识别为geojson
        if (geom.constructor.name === 'Object') {
            target = new Cesium.GeoJsonDataSource().load(geom)
        }

        // 读取最大缩放层级
        const config: any = {}
        if (options && 'maxZoom' in options) {
            config.maximumHeight = this.map.vc.getHeightFromZoom(
                options.maxZoom
            )
        }

        this.map.flyTo(target, config)
    }

    /**
     * @public
     * 像素点转地图坐标
     * @param {{x:Number, y:Number}} pixelPos
     * @returns {Number[]}
     */
    getMapCoords4Pixel(pixelPos: { x: number; y: number }): any {
        const pick = new Cesium.Cartesian2(pixelPos.x, pixelPos.y)
        return viewerPix2Coord(this.map, pick)
    }

    /**
     * @public
     * 地图坐标转像素点
     * @param {[Number, Number, Number?] | { lng: Number; lat: Number }} mapCoords
     * @returns {{x:Number, y:Number}}
     */
    getPixel4MapCoords(mapCoords: any): Cesium.Cartesian2 {
        const ll = toCentoid(mapCoords)
        return Cesium.SceneTransforms.wgs84ToWindowCoordinates(
            this.map.scene,
            Cesium.Cartesian3.fromDegrees(ll[0], ll[1], ll[2])
        )
    }

    /**
     * @public
     * 判断指定坐标是否在图上可见
     * @param {[Number, Number, Number?] | { lng: Number; lat: Number } | { x: Number; y: Number }} point
     * @returns {Boolean}
     */
    containPoint(point: any): boolean {
        return new (Cesium as any).EllipsoidalOccluder(
            Cesium.Ellipsoid.WGS84,
            this.map.camera.position
        ).isPointVisible(Cesium.Cartesian3.fromDegrees(...toCentoid(point)))
    }
}
